﻿//2019-3-26 郑捷 创建
#include "ModbusMasterRTU.h"
/*------------------------------------------------------------------------------------------------
MODBUSRTU  主站协议Ver1.0
---------------------------------------------------------------------------------------------------*/
#define microsLast(t) ((micros()+0xFFFFFFFF-t)%0xFFFFFFFF) //us计时器
#define millisLast(t) ((millis()+0xFFFFFFFF-t)%0xFFFFFFFF) //ms计时器

void ModbusMasterRTU::begin(HardwareSerial *SerialPort){
  pModbusPort=SerialPort;
  lastreadtime=micros();
  pData=0; //当前没有要读写的操作
  int baud=(*pModbusPort).baudRate();
  if (baud > 19200){  //波特率大于19200时进入条件
    characterTime = 50; //波特率大于19200时，0.1字符时间恒定为50us
  }else{
    characterTime = 1000000/baud;  //0.1字符时间
  }
}

unsigned char ModbusMasterRTU::readData(unsigned char PLCID,unsigned int DataStart,unsigned int DataNum,int *Data){
  if(DataNum<((bufferSize-5)/2)){
    cmd[0]=PLCID;
    cmd[1]=3;
    cmd[2]=DataStart>>8;
    cmd[3]=DataStart & 0xFF;
    cmd[4]=DataNum>>8;
    cmd[5]=DataNum & 0xFF;
    pData=Data;
    //准备发送帧
    memcpy(frame,cmd,6);
    needSendBytes=8;
    unsigned int crc16 = calculateCRC(&frame[0],needSendBytes-2);
    frame[needSendBytes-2] = crc16>>8;  //填写校验位
    frame[needSendBytes-1] = crc16 & 0xFF;
    //启动发送
    unsigned long starttime = millis(); //开始执行时间
    state=1; //从空闲状态开始 
    do{
      task();
    }while (state<5 && millisLast(starttime)<1000); //读取完或超时退出
    if (state==5) { //是读取完成返回
      return(err);
    }else{ //是超时返回
      return(0x40);
    }
  }else{  //数据量超过缓冲大小
    return(0x49);
  }
}
unsigned char ModbusMasterRTU::writeData(unsigned char PLCID,unsigned int DataStart,unsigned int DataNum,int *Data){
  if(DataNum<((bufferSize-9)/2)){
    cmd[0]=PLCID;
    cmd[1]=16;
    cmd[2]=DataStart>>8;
    cmd[3]=DataStart & 0xFF;
    cmd[4]=DataNum>>8;
    cmd[5]=DataNum & 0xFF;
    pData=Data;
    //准备发送帧
    memcpy(frame,cmd,6);
    frame[6]=DataNum*2;
    needSendBytes=9+DataNum*2;
    for(int i=0;i<DataNum;i+=2){ //填充数据
      frame[i+7]=(*pData)>>8;
      frame[i+8]=(*pData) & 0xFF;
      pData++;
    }
    unsigned int crc16 = calculateCRC(&frame[0],needSendBytes-2);
    frame[needSendBytes-2] = crc16>>8;  //填写校验位
    frame[needSendBytes-1] = crc16 & 0xFF;
    //启动发送
    unsigned long starttime = millis(); //开始执行时间
    state=1; //从空闲状态开始 
    do{
      task();
    }while (state<5 && millisLast(starttime)<1000); //读取完或超时退出
    if (state==5) { //是读取完成返回
      return(err);
    }else{ //是超时返回
      return(0x40);
    }
  }else{  //数据量超过缓冲大小
    return(0x49);
  }
}
unsigned char ModbusMasterRTU::test(unsigned char PLCID,unsigned int Temp){
  cmd[0]=PLCID;
  cmd[1]=8;
  cmd[2]=0;
  cmd[3]=0;
  cmd[4]=Temp>>8;
  cmd[5]=Temp & 0xFF;
  //准备发送帧
  memcpy(frame,cmd,6);
  needSendBytes=8;
  unsigned int crc16 = calculateCRC(&frame[0],needSendBytes-2);
  frame[needSendBytes-2] = crc16>>8;  //填写校验位
  frame[needSendBytes-1] = crc16 & 0xFF;
  //启动发送
  unsigned long starttime = millis(); //开始执行时间
  state=1; //从空闲状态开始 
  do{
    task();
  }while (state<5 && millisLast(starttime)<1000); //完或超时退出
  if (state==5) { //是数据完成返回
    return(err);
  }else{ //是超时返回
    return(0x40);
  }
}

void ModbusMasterRTU::task(void){
  //Serial.print("modbusDataSize=");Serial.println(modbusDataSize,DEC);
  //State状态变量说明：0=初始状态，1=空闲状态，2=接收状态，3=发送状态，4=控制状态，5=控制后等待状态
  if(state==0){ //初始状态
    if(microsLast(lastreadtime)>(characterTime*35)){//t3.5定时时间到
      (*pModbusPort).flush(); //清串口缓存
      state=1; //状态变为空闲
    }else{
      while((*pModbusPort).available()>0){ //如果串口收到数据
        (*pModbusPort).read(); //空读没用的数据
        lastreadtime=micros();  //接收到字符重启t3.5
      }
    }
  }
  if(state==1){ //空闲状态 
    if(needSendBytes>0){ //要求发送
      state=3; //状态变为发送
    }
    if((*pModbusPort).available()>0){ //串口接收到字符
      address=0; //开始接收第1个字符
      state=2; //状态变为接收
      lastreadtime=micros(); //重启t1.5和t3.5
    }
  }
  if(state==2){ //接收状态
    while((*pModbusPort).available()>0){ //收到串口数据
      if(address<bufferSize){ //接收的数据量应小于一帧数据的最大字节数量
        frame[address]=(*pModbusPort).read();
        address++;
      }else{  //条件不满足时直接清空缓冲区
        (*pModbusPort).read();
      }
      lastreadtime=micros();  //重启t1.5和t3.5
    }
    if(microsLast(lastreadtime)>(characterTime*15)){    //t1.5定时时间到
      state=4; //状态变为控制处理
    }
  }
  if(state==3){ //发送状态
    if(needSendBytes==0){
      if(microsLast(lastreadtime)>(characterTime*35)){      //t3.5定时时间到
        state=1; //状态变为空闲
      }
    }else{
      (*pModbusPort).write(&frame[0],needSendBytes);  //发送应答数据
      delay(characterTime*10);
      needSendBytes=0; //发送完毕清发送字节数
      lastreadtime=micros(); //重启t3.5
    }
  }
  if(state==4){ //控制和处理
    dataHandle(); 
    state=5; //状态变为等待状态
  }
  if(state==5){ //控制和处理后等待状态
    if(microsLast(lastreadtime)>(characterTime*35)){    //t3.5定时时间到
      //state=1; //状态变为空闲
    }
  }
}

void ModbusMasterRTU::dataHandle(void){
  if(frame[0]==cmd[0]){  //站号匹配
    unsigned char function=frame[1];  //读取功能码     
    unsigned int crc16 = ((frame[address - 2] << 8) | frame[address - 1]); //校验位
    if(calculateCRC(&frame[0],address - 2)==crc16){  //数据校验通过
      if(function==3){  //读寄存器0x03的返回
        for(int i=0;i<frame[2];i+=2){ //根据返回的字节数填充数据
          *pData=(frame[i+3]<<8)|frame[i+4];
          pData++;
        }
      }else if(function==16){ //写多寄存器0x10的返回
        if(frame[2]==cmd[2] && frame[3]==cmd[3] && frame[4]==cmd[4] && frame[5]==cmd[5]){ //地址都通过
          ;
        }else{
          err=0x47; //写指令返回的地址无法通过
        }
      }else if(function==8){  //通讯侦测
        if(frame[2]==0 && frame[3]==0 && frame[4]==cmd[4] && frame[5]==cmd[5]){ //地址都通过
          ;
        }else{
          err=0x47; //通讯侦测返回无法通过
        }
      }else{  //不能识别的功能码，最大的可能是错啦
        err=frame[2]|0x80;  //返回错误代码
      }
    }else{
      err=0x43; //校验错误
    }
  }else{
    err=0x41; //返回的站号不匹配
  }
}

//CRC校验函数
//参数1:待校验数组的起始地址
//参数2:待校验数组的长度
//返回值CRC校验结果,16位,低字节在前
unsigned int ModbusMasterRTU::calculateCRC1(unsigned char* regs,unsigned char arraySize){
  unsigned int temp, temp2, flag;
  temp = 0xFFFF;
  for (unsigned char i = 0; i < arraySize; i++){
    temp = temp ^ *(regs+i);
    for (unsigned char j = 1; j <= 8; j++){
      flag = temp & 0x0001;
      temp >>= 1;
      if (flag)
        temp ^= 0xA001;
    }
  }
  temp2 = temp >> 8;
  temp = (temp << 8) | temp2;
  temp &= 0xFFFF; 
  return temp; 
}

//CRC校验函数（查表法）
/* CRC 高位字节值表 */    
const unsigned char c_aucCRCHi[] = {    
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,    
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,    
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,    
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,    
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,    
0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,    
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,    
0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,    
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,    
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,    
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,    
0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,    
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,    
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,    
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,    
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,    
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,    
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,    
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,    
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,    
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,    
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,    
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,    
0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,    
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,    
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40    
} ;    
   
/* CRC低位字节值表*/    
const unsigned char c_aucCRCLo[] = {    
0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,    
0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,    
0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,    
0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,    
0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,    
0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,    
0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,    
0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,    
0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,    
0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,    
0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,    
0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,    
0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,    
0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,    
0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,    
0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,    
0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,    
0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,    
0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,    
0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,    
0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,    
0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,    
0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,    
0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,    
0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,    
0x43, 0x83, 0x41, 0x81, 0x80, 0x40    
} ;   

unsigned int ModbusMasterRTU::calculateCRC(unsigned char* regs,unsigned char arraySize){    
  unsigned int uiCRCHi = 0xFF ;              /* 高CRC字节初始化  */    
  unsigned int uiCRCLo = 0xFF ;              /* 低CRC 字节初始化 */    
  unsigned char uIndex ;                      /* CRC循环中的索引  */    
  while (arraySize--){                  /* 传输消息缓冲区   */    
    uIndex = (char)uiCRCHi ^ *regs++ ; /* 计算CRC          */    
    uiCRCHi = uiCRCLo ^ (int)c_aucCRCHi[uIndex] ;    
    uiCRCLo = (int)c_aucCRCLo[uIndex] ;    
  }    
  return (uiCRCHi << 8 | uiCRCLo) ;    
}    
